Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update section on client authentication #216

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

eric-murray
Copy link
Collaborator

What type of PR is this?

  • enhancement/feature

What this PR does / why we need it:

The current specification for client authentication does not limit the lifetimes of the signed JWT. This presents a security risk, and further complicates implementations as there is an additional requirement that the authorisation server should reject duplicate JWTs.

This PR proposes that:

  • the signed JWT must include the iat claim
  • token lifetimes must be limited to 300 seconds
  • tokens with longer lifetimes must be rejected by the authorisation server

Which issue(s) this PR fixes:

Fixes #208

Special notes for reviewers:

None

Changelog input

 release-note
 - Add restriction on maximum JWT lifetime for client authentication

Additional documentation

See proposed changes for documentation links

@eric-murray
Copy link
Collaborator Author

Specifying that they may return an error if the expiration time is longer also ensures interoperability.

No, it doesn't. An API consumer may get used to tokens with a longer lifetime being accepted, and then one day they are rejected because they are dealing with a different authorisation server.

However, mandating the inclusion of a claim that is optional in the standard itself does not

The CAMARA specification is a separate specification to OIDC, albeit one defined by differences to that standard. Any CAMARA client or authorisation server must meet the CAMARA specification, and not just vanilla OIDC. What you are saying is that any API consumer that does not adopt the CAMARA standard but only the OIDC standard will have interoperability issues. Well, yes they will, which is why we don't say "just use OIDC".

There are already changes relative to OIDC in the CAMARA standard that mandate features that are optional in OIDC. This would be another such change - a feature that is optional for OIDC is mandatory for CAMARA.

What would happen to existing authorization server implementations that follow OIDC and the iat claim is considered optional?

iat is already an optional parameter, so no OIDC compliant authorisation server should reject a token that includes the iat claim.

What would happen to applications that are already integrated and do not send the iat claim?

API definitions are linked to a specific release of the CAMARA working group standards. This would be a change to the next release of the ICM standards, and so existing API definitions are not affected. The client would only need to upgrade to support the next release of the API.

According to your proposal, what would be the behavior of an authorization server that receives a request without the iat claim?

The authorisation server should reject the request as not being CAMARA compliant. However, no "authorisation server police" are going to come around and shutdown the server. But an API consumer used to having tokens accepted without the iat claim would then be confused when other authorisation servers start rejecting them.

That is the situation we are trying to avoid by having an "interoperability profile".

@palmerabollo
Copy link

I think we can have the best of both worlds (ensure interoperability + minimize deviation from standards + avoid breaking existing API consumers when possible) if we:

  1. Keep iat as an optional field for API consumers

In addition to the mandated claims, the signed JWT SHOULD also include the iat (issued at) claim

  1. Maintain the restriction on expiration time as mandatory (MUST) , using the authorization server's current time as a default when iat is not present.

The difference between the exp (expires at) and iat (or the authorization server's current time, if absent) claims MUST be no more than 300 seconds [...]

JWTs should be used shortly after they are generated; 300 seconds is a generous interval, so I don't think defaulting to the current time is an issue.

@eric-murray What do you think?

Some telcos don't have a dedicated Authorization Server exclusively for issuing tokens for CAMARA APIs. This is one of the reasons why making standard JWTs not valid is problematic.

@eric-murray
Copy link
Collaborator Author

Hi @palmerabollo

So I'm fine to keep iat optional - this just slightly complicates the authorisation server logic for rejecting tokens, but isn't a big deal. Also fine to make 300 second token lifetime mandatory as that was the original proposal.

I'd prefer to keep the statement "JWTs with a longer lifetime SHALL be rejected by the authorisation server" because this addresses @MarkCornall's concern about specifying consistent behaviour across implementations.

Now I accept that there will be authorisation server implementations that will anyway accept tokens with a longer lifetime. And that's fine - as I said, no "authorisation server police" are going to come around and shut it down. Specifications such as this one are written primarily for clients so that they understand how to construct their requests, and what responses they can expect. API providers know the game and know what they can get away with at the edges of a standard without breaking it.

So the statement "JWTs with a longer lifetime SHALL be rejected by the authorisation server" would be telling the client what will happen if their token's lifetime is too long. Whether that actually happens in all cases is not so important, though we do want to avoid the situation where most authorisation servers accept longer lived tokens and clients get used to that behaviour.

@palmerabollo
Copy link

Agree, thanks Eric.

jpengar
jpengar previously approved these changes Oct 21, 2024
Copy link
Collaborator

@jpengar jpengar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM considering #216 (comment)

@AxelNennker
Copy link
Collaborator

What is the benefit of having iat?

Why not just use exp and define that exp-now<300 seconds?

Also why mention all the different endpoints? Why not just state that the lifetime of the jwt used in client_credentials flow must be shorter than 300 seconds.

The current text seems unnecessarily complicated.

[OIDC Client Authentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
The API consumer MUST authenticate with the authorisation server using `private_key_jwt`, as specified in [OIDC Client Authentication](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication). In addition to the mandated claims, the signed JWT SHOULD also include the `iat` (issued at) claim.

The JWT lifetime MUST be no more than 300 seconds, measured as the difference between the `exp` (expires at) claim and the token creation time (the value of the `iat` claim, whether present in the token or not). JWTs with a longer lifetime SHALL be rejected by the authorisation server using the `invalid_client` error code as specified in [IETF RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2) for the `/token` endpoint, and as specified in [OIDC CIBA Core](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#rfc.section.13) for the `/bc-authorize` endpoint. If the JWT does not include the `iat` claim, token lifetime SHALL be measured relative to time of receipt.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The JWT lifetime MUST be no more than 300 seconds, measured as the difference between the `exp` (expires at) claim and the token creation time (the value of the `iat` claim, whether present in the token or not). JWTs with a longer lifetime SHALL be rejected by the authorisation server using the `invalid_client` error code as specified in [IETF RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2) for the `/token` endpoint, and as specified in [OIDC CIBA Core](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#rfc.section.13) for the `/bc-authorize` endpoint. If the JWT does not include the `iat` claim, token lifetime SHALL be measured relative to time of receipt.
The JWT lifetime as indicated by the `exp` field MUST be no more than 300 seconds. Token lifetime SHALL be measured relative to time of receipt.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are effectively saying "The iat claim, if present, MUST be ignored". I can't agree with that.

Tokens MAY (indeed SHOULD) include the iat claim, and that should be the primary determinant of token lifetime if present. One day, hopefully, all JWTs used for authentication will include the iat claim.

@eric-murray
Copy link
Collaborator Author

@AxelNennker

What is the benefit of having iat

Why not just use exp and define that exp-now<300 seconds?

CIBA Core made the mistake of making iat optional rather than mandatory for private_key_jwt, and we need to live with that because of the "existing implementations" argument. If iat was mandatory, key lifetime would be straightforward for both API consumer and the authorisation server to calculate for any JWT.

JWTs without an iat claim could have been created at any time. This increases the risk that the API consumer "stockpiled" them some time ago, and they have since leaked. By specifying that the API consumer should limit the token lifetime to 300 seconds when they create them, we can reduce (but not eliminate) this risk.

I see that OIDC CIBA avoided this mistake for signed authentication requests, so well done there.

Also why mention all the different endpoints?

From ICM Meeting Minutes:

Outcome:

Eric will draft more detailed text to specify exact requirements and error responses for various token lifetime scenarios. The group will review this in the next meeting to finalize.

So the request was to specify error responses for the different endpoints as the relevant standards do not include "token lifetime too long" as an explicit error example. But I agree it may now be better to document this in #220.

Why not just state that the lifetime of the jwt used in client_credentials flow must be shorter than 300 seconds.

If the standard is using the term "lifetime", we need to define what is meant by "lifetime", and I am defining it as the difference between the exp and iat claims of the token. Doesn't get much simpler than that. The complexity arises solely from the iat claim not being mandatory in the JWT itself, but the concept remains valid even for API consumers who do not explicitly include the iat claim in their token.

"client_credentials flow" is not the correct term here

@eric-murray
Copy link
Collaborator Author

Removed wording on error responses, as that is now covered by #220

@AxelNennker
Copy link
Collaborator

JWTs without an iat claim could have been created at any time. This increases the risk that the API consumer "stockpiled" them some time ago, and they have since leaked. By specifying that the API consumer should limit the token lifetime to 300 seconds when they create them, we can reduce (but not eliminate) this risk.

How does iat help against creating stockpiled jwts or stockpiled jwts being misused after they were stolen?

The API consumer is controlling JWT creation. So, they could create 1000 JWT for future use.

some pseudo-code that creates JWTs with lifetime 300 that can run at the API consumer's site at anytime:

start = now
for iat=start do
  create JWT with iat and exp=iat+300
  sign JWT
  store JWT (somewhere where it can be stolen)
  iat=iat+300
  if iat>start+3000000 break

Now the API consumer has many JWTs with iat which still can be stolen and misused.

I feel I am missing something. Sorry, if I am thick. Which threat scenario is iat protecting against?

@eric-murray
Copy link
Collaborator Author

Now the API consumer has many JWTs with iat which still can be stolen and misused.

I feel I am missing something. Sorry, if I am thick. Which threat scenario is iat protecting against?

Well, it clearly doesn't eliminate the risk of tokens being stolen and misused, but may reduce the number that are available to be stolen.

Let's say an API consumer does not want to create signed JWTs "on the fly", but rather generate them overnight using a batch process, and then use them throughout the next day by pulling one off the stack when required. Even with the new 300 second maximum token lifetime rule, they could still create multiple batches overnight, with exp times incremented by 300 seconds for each batch. They would just need to remember to discard any unused tokens as each batch expired.

But if they included the iat in the tokens, then they could only create a batch for the next 300 seconds. Any created with a later exp time would be invalid and known to be invalid when they were created (as exp - iat would be > 300). So the maximum "stockpile" size would be 300 seconds worth of tokens, and the consequences of their theft would only last for the next 300 seconds.

And maybe the need to generate a new batch every 300 seconds just might persuade the API consumer to generate them on demand, as they should.

It's a small point, but a point nonetheless.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proposal for CAMARA mandated minimum acceptable JWT token lifetime
6 participants